﻿/* 
   Copyright 2011 LUCA Studios LLC & John Evans

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

namespace LUCAStudios.Silverlight.Controls
{
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Windows;
    using DragDrop;
    using System.Linq;

    /// <summary>
    /// A base clase for LUCA UI container controls
    /// </summary>
    public abstract class ContainerBase : ControlBase, IDragDropTarget
    {
        /// <summary>
        /// Constructor
        /// </summary>
        protected ContainerBase()
        {
            Children = new LUCAObservableCollection<ControlBase>();
            Children.CollectionChanged += ItemCollectionChanged;
        }

        /// <summary>
        /// Backing property for the Children collection
        /// </summary>
        public static readonly DependencyProperty ChildrenProperty =
            DependencyProperty.Register("Children", typeof(LUCAObservableCollection<ControlBase>),
                                        typeof(ContainerBase), new PropertyMetadata(null));

        /// <summary>
        /// Dependency Property representing a collection of ControlBase object in the container
        /// </summary>
        public LUCAObservableCollection<ControlBase> Children
        {
            get { return (LUCAObservableCollection<ControlBase>)GetValue(ChildrenProperty); }
            set { SetValue(ChildrenProperty, value); }
        }

        internal double LastKnownWidth { get; set; }
        internal double LastKnownHeight { get; set; }

        private void ItemCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null && e.NewItems.OfType<ControlBase>().Any())
            {
                foreach (var i in e.NewItems)
                {
                    ((ControlBase)i).ContainerContext = this;
                }
            }

            OnCollectionChanged(e);
        }

        /// <summary>
        /// Called when the Children collection changes
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
        }

        /// <summary>
        /// Performs a safe remove of a given child from the collection.  
        /// Preferable over direct remove from the Children collection itself,
        /// because this method can be overriden by derived controls
        /// </summary>
        /// <param name="childToRemove"></param>
        public virtual void RemoveChild(ControlBase childToRemove)
        {
            Children.Remove(childToRemove);
        }

        /// <summary>
        /// Performs a framework-safe add of a child to the collection
        /// Preferable over direct Add to the Children collection itself,
        /// because this method can be overriden by derived controls
        /// </summary>
        /// <param name="childToAdd"></param>
        public virtual void AddChild(ControlBase childToAdd)
        {
            Children.Add(childToAdd);
        }

        /// <summary>
        /// Performs a framework-safe insert into the Children collection
        /// Preferable over direct Insert to the Children collection itself,
        /// because this method can be overriden by derived controls
        /// </summary>
        /// <param name="insertAtIndexOf"></param>
        /// <param name="childToInsert"></param>
        public virtual void InsertChild(int insertAtIndexOf, ControlBase childToInsert)
        {
            Children.Insert(insertAtIndexOf, childToInsert);
        }

        #region IDragDropTarget Members

        private readonly List<DropZoneBase> _emptyList = new List<DropZoneBase>();

        /// <summary>
        /// Requires the control to return drop zones when a drag operation begins
        /// </summary>
        /// <param name="dropData">The IDragDropSource.DragDropData information from the object that was dropped</param>
        /// <returns></returns>
        public virtual IEnumerable<DropZoneBase> GetDropZones(object dropData)
        {
            return _emptyList;
        }

        /// <summary>
        /// Provides a hook for when an IDragDropSource enters a dropzone associated with this IDragDropTarget
        /// </summary>
        /// <param name="dropZone"></param>
        public virtual void OnDragDropEnter(DropZoneBase dropZone)
        {

        }

        /// <summary>
        /// Provides a hook for when an IDragDropSource leaves a dropzone associated with this IDragDropTarget
        /// </summary>
        public virtual void OnDragDropLeave()
        {

        }

        /// <summary>
        /// Called when a IDragDropSource is dropped on the IDragDropTarget (in any defined drop zone {see 'GetDropZones() method'})
        /// </summary>
        /// <param name="dropZone">The dropzone object that the IDragDropSource dropped into</param>
        /// <param name="dropData">The data associated with the IDragDropSource.DragDropData.  Usually the IDragDropSource object itself.</param>
        public virtual void OnDrop(DropZoneBase dropZone, object dropData)
        {

        }

        #endregion
    }
}